#include <memory>
#include <stack>

#include "Binding/BoundBlock.hpp"
#include "Binding/BoundError.hpp"
#include "Binding/BoundExpressionStatement.hpp"
#include "Binding/BoundForStatement.hpp"
#include "DiagnosticBag.hpp"

#include "Binding/Binder.hpp"
#include "Binding/BoundAssignment.hpp"
#include "Binding/BoundBinary.hpp"
#include "Binding/BoundExpression.hpp"
#include "Binding/BoundGlobalScope.hpp"
#include "Binding/BoundIfStatement.hpp"
#include "Binding/BoundLiteral.hpp"
#include "Binding/BoundScope.hpp"
#include "Binding/BoundUnary.hpp"
#include "Binding/BoundVariable.hpp"
#include "Binding/BoundVariableDeclaration.hpp"
#include "Binding/BoundWhileStatement.hpp"

#include "Syntax/Expression/AssignmentExpression.hpp"
#include "Syntax/Expression/BinaryExpression.hpp"
#include "Syntax/Expression/Expression.hpp"
#include "Syntax/Expression/LiteralExpression.hpp"
#include "Syntax/Expression/NameExpression.hpp"
#include "Syntax/Expression/UnaryExpression.hpp"
#include <Syntax/Expression/ParenthesizedExpression.hpp>

#include "Syntax/Object/Integer.hpp"
#include "Syntax/Object/Object.hpp"

#include "Syntax/Object/TypeKind.hpp"
#include "Syntax/Statement/BlockStatement.hpp"
#include "Syntax/Statement/ExpressionStatement.hpp"
#include "Syntax/Statement/ForStatement.hpp"
#include "Syntax/Statement/IfStatement.hpp"
#include "Syntax/Statement/Statement.hpp"
#include "Syntax/Statement/VariableDeclaration.hpp"
#include "Syntax/Statement/WhileStatement.hpp"

#include "Syntax/CompilationUnit.hpp"

#include "Symbol/TypeSymbol.hpp"
#include "Symbols/VariableSymbol.hpp"

Binder::Binder(SharedBoundScope parent)
{
    this->Scope       = (parent != nullptr) ? parent : std::make_shared<BoundScope>(nullptr);

    this->diagnostics = std::make_shared<DiagnosticBag>();
}
SharedBoundGlobalScope Binder::BindGlobalScope(SharedBoundGlobalScope previous, SharedCompilationUnit syntax)
{
    auto   parentScope = CreateParentScope(previous);
    Binder binder(parentScope);
    auto   expression  = binder.BindStatement(syntax->Statement);
    auto   variables   = binder.Scope->GetDeclaredVariables();
    auto   diagnostics = binder.diagnostics;

    if (previous != nullptr)
        diagnostics->InsertRange(previous->Diagnostics);

    return std::make_shared<BoundGlobalScope>(previous, diagnostics, variables, expression);
}
SharedBoundScope Binder::CreateParentScope(SharedBoundGlobalScope previous)
{
    std::stack<SharedBoundGlobalScope> gstack;

    // 将所有的SharedBoundGlobalScope 暂时保存;
    while (previous != nullptr)
    {
        gstack.push(previous);

        previous = previous->Previous;
    }

    SharedBoundScope parent = nullptr;
    // 反序 - 声明变量,最上层的最先声明;
    while (gstack.size() > 0)
    {
        previous = gstack.top();
        gstack.pop();
        auto scope = std::make_shared<BoundScope>(parent);

        for (auto v : previous->Variables)
        {
            scope->TryDeclare(v);
        }
        parent = scope;
    }

    return parent;
}

SharedBoundStatement Binder::BindStatement(SharedStatement syntax)
{
    switch (syntax->GetKind())
    {
    case SyntaxKind::BlockStatement :
        return this->BindBlockStatement((BlockStatementSyntax *)syntax.get());
    case SyntaxKind::ExpressionStatement :
        return this->BindExpressionStatement((ExpressionStatementSyntax *)syntax.get());
    case SyntaxKind::VariableDeclaration :
        return this->BindVeriableDeclaration((VariableDeclarationSyntax *)syntax.get());
    case SyntaxKind::IfStatement :
        return this->BindIfStatement((IfStatementSyntax *)syntax.get());
    case SyntaxKind::WhileStatement :
        return this->BindWhileStatement((WhileStatementSyntax *)syntax.get());
    case SyntaxKind::ForStatement :
        return this->BindForStatement((ForStatementSyntax *)syntax.get());
    default :
        throw "Unexpected syntax " + SyntaxKindName::GetTokenName(syntax->GetKind());
    }
}
SharedBoundStatement Binder::BindVeriableDeclaration(VariableDeclarationSyntax *syntax)
{
    auto name        = syntax->Identifier->GetName();

    auto isReadOnly  = syntax->Keyword->GetKind() == SyntaxKind::Let;

    auto initializer = this->BindExpression(syntax->Initializer);

    auto variable    = this->BindVariable(syntax->Identifier, isReadOnly, initializer->GetType());

    return std::make_shared<BoundVariableDeclaration>(variable, initializer);
}
SharedBoundStatement Binder::BindBlockStatement(BlockStatementSyntax *syntax)
{
    std::vector<SharedBoundStatement> statements;

    //  创建一个新的变量访问性范围;
    this->Scope = std::make_shared<BoundScope>(this->Scope);

    for (auto statementSyntax : syntax->Statements)
    {
        auto statement = this->BindStatement(statementSyntax);
        statements.push_back(statement);
    }
    // 恢复;
    this->Scope = this->Scope->Parent;

    return std::make_shared<BoundBlockStatement>(statements);
}
SharedBoundStatement Binder::BindExpressionStatement(ExpressionStatementSyntax *syntax)
{
    auto expression = this->BindExpression(syntax->Expression);
    return std::make_shared<BoundExpressionStatement>(expression);
}

SharedBoundStatement Binder::BindIfStatement(IfStatementSyntax *syntax)
{
    auto condition     = this->BindExpression(syntax->Condition, TypeSymbol::GetBool());
    auto thenStatement = this->BindStatement(syntax->ThanStatement);
    auto elseStatement = syntax->ElseClause == nullptr ? nullptr : this->BindStatement(syntax->ElseClause->ElseStatement);

    return std::make_shared<BoundIfStatement>(condition, thenStatement, elseStatement);
}
SharedBoundStatement Binder::BindWhileStatement(WhileStatementSyntax *syntax)
{
    auto condition     = this->BindExpression(syntax->Condition, TypeSymbol::GetBool());
    auto bodyStatement = this->BindStatement(syntax->BodyStatement);
    return std::make_shared<BoundWhileStatement>(condition, bodyStatement);
}
SharedBoundStatement Binder::BindForStatement(ForStatementSyntax *syntax)
{
    auto lowerBound = this->BindExpression(syntax->LowerBound, TypeSymbol::GetInt());
    auto upperBound = this->BindExpression(syntax->UpperBound, TypeSymbol::GetInt());

    //  创建一个新的变量访问性范围;
    this->Scope     = std::make_shared<BoundScope>(this->Scope);
    auto name       = syntax->Identifier->GetName();

    auto variable    = this->BindVariable(syntax->Identifier,true, TypeSymbol::GetInt());

    auto body   = this->BindStatement(syntax->Body);
    // 恢复;
    this->Scope = this->Scope->Parent;
    return std::make_shared<BoundForStatement>(variable, lowerBound, upperBound, body);
}
SharedBoundExpression Binder::BindExpression(SharedExpression syntax, SharedTypeSymbol targetType)
{
    auto result = this->BindExpression(syntax);

    if ((targetType != TypeSymbol::GetError()) && (result->GetType() != TypeSymbol::GetError()) && (result->GetType() != targetType))
    {
        // TODO: 上面的公式尚未验证 2024年3月9日 21时27分22秒；

        // TODO: node中未实现textSpan 方法,所以这儿新建一个置零;
        this->diagnostics->ReportCannotConvert(TextSpan(0, 0), result->GetType(), targetType);
    }
    return result;
}
SharedBoundExpression Binder::BindExpression(SharedExpression syntax)
{
    switch (syntax->GetKind())
    {
    case SyntaxKind::ParenthesizedExpression :
        return this->BindParenthesizedExpression((ParenthesizedExpressionSyntax *)syntax.get());
    case SyntaxKind::LiteralExpression :
        return this->BindLiteralExpression((LiteralExpressionSyntax *)syntax.get());
    case SyntaxKind::UnaryExpression :
        return this->BindUnaryExpression((UnaryExpressionSyntax *)syntax.get());
    case SyntaxKind::BinaryExpression :
        return this->BindBinaryExpression((BinaryExpressionSyntax *)syntax.get());
    case SyntaxKind::NameExpression :
        return this->BindNameExpression((NameExpressionSyntax *)syntax.get());
    case SyntaxKind::AssignmentExpression :
        return this->BindAssignmentExpression((AssignmentExpressionSyntax *)syntax.get());
    default :
        throw "Unexpected syntax " + SyntaxKindName::GetTokenName(syntax->GetKind());
    }
}
SharedBoundExpression Binder::BindLiteralExpression(LiteralExpressionSyntax *syntax)
{
    return std::make_shared<BoundLiteralExpression>(syntax->Value);
}
SharedBoundExpression Binder::BindParenthesizedExpression(ParenthesizedExpressionSyntax *syntax)
{
    return this->BindExpression(syntax->Expression);
}

SharedBoundExpression Binder::BindNameExpression(NameExpressionSyntax *syntax)
{
    auto name = syntax->IdentifierToken->GetTextName();

    // 空文本 直接返回0；
    if (syntax->IdentifierToken->IsMissing())
    {
        return std::make_shared<BoundErrorExpression>();
    }
    auto variable = this->Scope->TryLookup(name);
    if (variable == nullptr)
    {
        // 变量未定义;
        this->diagnostics->ReportUndefinedName(syntax->IdentifierToken->GetTextSpan(), name);
        return std::make_shared<BoundErrorExpression>();
    }
    return std::make_shared<BoundVariableExpression>(variable);
}

SharedBoundExpression Binder::BindAssignmentExpression(AssignmentExpressionSyntax *syntax)
{
    auto name            = syntax->IdentifierToken->GetTextName();
    auto boundExpression = BindExpression(syntax->Expression);

    auto variable        = this->Scope->TryLookup(name);
    if (variable == nullptr)
    {
        // 变量未定义;
        this->diagnostics->ReportUndefinedName(syntax->IdentifierToken->GetTextSpan(), name);
        return boundExpression;
    }
    if (variable->IsReadOnly())
        this->diagnostics->ReportCannotAssign(syntax->EqualsToken->GetTextSpan(), name);

    if (boundExpression->GetType() != variable->GetType())
    {
        this->diagnostics->ReportCannotConvert(syntax->IdentifierToken->GetTextSpan(), boundExpression->GetType(), variable->GetType());
        return boundExpression;
    }

    return std::make_shared<BoundAssignmentExpression>(variable, boundExpression);
}
SharedBoundExpression Binder::BindUnaryExpression(UnaryExpressionSyntax *syntax)
{
    auto boundOperand = this->BindExpression(syntax->Operand);

    if (boundOperand->GetType() == TypeSymbol::GetError())
    {
        // TODO: 上面的公式尚未验证 2024年3月9日 21时27分22秒；
        return std::make_shared<BoundErrorExpression>();
    }
    auto boundOperatorKind = BoundUnaryOperator::Bind(syntax->OperatorToken->GetKind(), boundOperand->GetType());

    if (boundOperatorKind == nullptr)
    {
        this->diagnostics->ReportUndefinedUnaryOperator(syntax->OperatorToken->GetTextSpan(), syntax->OperatorToken->GetTextName(), boundOperand->GetType());

        return std::make_shared<BoundErrorExpression>();
    }
    return std::make_shared<BoundUnaryExpression>(boundOperatorKind, boundOperand);
}

SharedBoundExpression Binder::BindBinaryExpression(BinaryExpressionSyntax *syntax)
{
    auto boundLeft  = this->BindExpression(syntax->Left);
    auto boundRight = this->BindExpression(syntax->Right);

    if ((boundLeft->GetType() == TypeSymbol::GetError()) || (boundRight->GetType() == TypeSymbol::GetError()))
    {
        // TODO: 上面的公式尚未验证 2024年3月9日 21时27分22秒；
        return std::make_shared<BoundErrorExpression>();
    }

    auto boundOperatorKind = BoundBinaryOperator::Bind(syntax->OperatorToken->GetKind(), boundLeft->GetType(), boundRight->GetType());
    if (boundOperatorKind == nullptr)
    {
        this->diagnostics->ReportUndefinedBinaryOperator(
            syntax->OperatorToken->GetTextSpan(), syntax->OperatorToken->GetTextName(), boundLeft->GetType(), boundRight->GetType());
        return std::make_shared<BoundErrorExpression>();
    }
    return std::make_shared<BoundBinaryExpression>(boundLeft, boundOperatorKind, boundRight);
}

SharedVariableSymbol Binder::BindVariable(SharedToken identifier, bool isReadOnly, SharedTypeSymbol type)
{
    auto name     = identifier->GetName();
    auto variable = std::make_shared<VariableSymbol>(name, isReadOnly, type);

    if (!this->Scope->TryDeclare(variable))
        this->diagnostics->ReportVariableAlreadyDeclared(identifier->GetTextSpan(), name);
    return variable;
}